package com.itheima.spring.aop;

import com.itheima.spring.aop.advisor.Advisor;
import com.itheima.spring.beans.BeanDefinition;
import com.itheima.spring.beans.BeanFactory;
import com.itheima.spring.beans.DefaultBeanFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;

public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor {

    private static final Log logger = LogFactory.getLog(CglibDynamicAopProxy.class);

    private static Enhancer enhancer = new Enhancer();

    private Object target;
    private String beanName;
    private List<Advisor> matchAdvisors;
    private BeanFactory beanFactory;

    public CglibDynamicAopProxy(Object target, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
        this.target = target;
        this.beanName = beanName;
        this.matchAdvisors = matchAdvisors;
        this.beanFactory = beanFactory;
    }


    /**
     * 创建代理对象
     * @return
     */
    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("为" + target + "创建cglib代理。");
        }
        Class<?> superClass = this.target.getClass();
        enhancer.setSuperclass(superClass);
        enhancer.setInterfaces(this.getClass().getInterfaces());
        enhancer.setCallback(this);

        Constructor<?> constructor = null;
        try {
            constructor = superClass.getConstructor(new Class<?>[]{});
        }catch (Exception e){

        }
        if(constructor != null){
           return enhancer.create();
        }else {
            //没有无参构造函数时,从BeanDefinition里面获取构造参数的类型和值进行增强
            BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName);
            return enhancer.create(bd.getConstructor().getParameterTypes(),bd.getConstructorArgumentRealValues());
        }
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        return AopProxyUtils.applyAdvices(target,method,args,matchAdvisors,proxy,beanFactory);
    }
}
